﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Portfel;

namespace PageNavigator
{
    /// <summary>
    /// Singleton class to navigate between pages and passing parameters
    /// </summary>
    public sealed class Navigator
    {
        /// <summary>
        /// Static object instance
        /// </summary>
        private static Navigator _instance;
        /// <summary>
        /// Navigation service instance
        /// </summary>
        private readonly NavigationService _navigationService;
        /// <summary>
        /// Dictionaty with passed objects
        /// </summary>
        private readonly Dictionary<string, object> _dict = new Dictionary<string, object>();
        private const string TheKey = "_data_key_";
        /// <summary>
        /// If set to true, page from which navigation begins, will be removed.
        /// One can imagine this as removing current page from stack
        /// </summary>
        private bool _removeCurrentPageFromStack;

        private Navigator()
        {
            this._navigationService = ((Application.Current as App).RootFrame.Content as Page).NavigationService;
            _navigationService.Navigated += delegate
            {
                if (_removeCurrentPageFromStack)
                    _navigationService.RemoveBackEntry();
                _removeCurrentPageFromStack = false;
            };
        }

        /// <summary>
        /// Returns the instance of Navigator
        /// </summary>
        public static Navigator Instance
        {
            get
            {
                return _instance ?? (_instance = new Navigator());
            }
        }

        /// <summary>
        /// Navigates to given page
        /// </summary>
        /// <param name="pageName">name of page</param>
        /// <param name="data">object to pass</param>
        /// <param name="queryString">string to be added to query</param>
        /// <param name="removeCurrentPageFromStack">if yes, then current page will be removed from stack</param>
        public void Navigate(string pageName, object data, string queryString, bool removeCurrentPageFromStack)
        {
            this._removeCurrentPageFromStack = removeCurrentPageFromStack;

            var guid = Guid.NewGuid().ToString();

            if (data != null)
            {
                _dict.Add(guid, data);
            }

            string uri;
            if (string.IsNullOrEmpty(queryString))
            {
                uri = string.Format("{0}?{1}={2}", pageName, TheKey, guid);
            }
            else
            {
                uri = string.Format("{0}?{1}={2}&{3}", pageName, TheKey, guid, queryString);
            }

            _navigationService.Navigate(new Uri(uri, UriKind.Relative));
        }

        /// <summary>
        /// Returns object previously stored in dict
        /// </summary>
        /// <param name="navigationContext"></param>
        /// <returns>object or null if none object was stored</returns>
        public object GetData(NavigationContext navigationContext)
        {
            if (!navigationContext.QueryString.Keys.Contains(TheKey))
                return null;

            var guid = navigationContext.QueryString[TheKey];

            object data = null;

            if (_dict.TryGetValue(guid, out data))
            {
                _dict.Remove(guid);
            }

            return data;
        }

        /// <summary>
        /// Returns object of given type
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="navigationContext">current page navigation context</param>
        /// <returns>object, or null if nono object was previously stored</returns>
        public T GetData<T>(NavigationContext navigationContext)
        {
            return (T)GetData(navigationContext);
        }

        /// <summary>
        /// Navigates to the page typeof T without any parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void Navigate<T>()
            where T : PhoneApplicationPage
        {
            Navigate(string.Format("/{0}.xaml", typeof(T).Name), null, string.Empty, false);
        }

        /// <summary>
        /// Navigates to the page typeof T without any parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void Navigate<T>(bool removeCurrentPageFromStack)
            where T : PhoneApplicationPage
        {
            Navigate(string.Format("/{0}.xaml", typeof(T).Name), null, string.Empty, removeCurrentPageFromStack);
        }

        /// <summary>
        /// Navigates to the page T with parameter data
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Any object</param>
        public void Navigate<T>(object data)
            where T : PhoneApplicationPage
        {
            Navigate(string.Format("/{0}.xaml", typeof(T).Name), data, string.Empty, false);
        }

        /// <summary>
        /// Navigates to the page T with parameter data
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Any object</param>
        /// <param name="removeCurrentPageFromStack"></param>
        public void Navigate<T>(object data, bool removeCurrentPageFromStack)
            where T : PhoneApplicationPage
        {
            Navigate(string.Format("/{0}.xaml", typeof(T).Name), data, string.Empty, removeCurrentPageFromStack);
        }

        /// <summary>
        /// Navigates to the page T with parameter data
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Any object</param>
        /// <param name="queryString">additional query string params</param>
        public void Navigate<T>(object data, string queryString)
            where T : PhoneApplicationPage
        {
            this.Navigate(string.Format("/{0}.xaml", typeof(T).Name), data, queryString, false);
        }

        /// <summary>
        /// Navigates to the page T with parameter data
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Any object</param>
        /// <param name="queryString">additional query string params</param>
        /// <param name="removeCurrentPageFromStack"></param>
        public void Navigate<T>(object data, string queryString, bool removeCurrentPageFromStack)
            where T : PhoneApplicationPage
        {
            this.Navigate(string.Format("/{0}.xaml", typeof(T).Name), data, queryString, removeCurrentPageFromStack);
        }
    }
}
